import {ResolveType, RejectType, Executor, Status, Fn} from "../types";

class Promise<T = any> {
    public resolve!: ResolveType;
    public reject!: RejectType;
    public status!: string;
    public resolveValue!: any;
    public rejectValue!: any;
    // 保存成功状态要执行的函数
    public resolveCallbacks: Array<Fn> = [];
    // 保存失败状态要执行的函数
    public rejectCallbacks: Array<Fn> = [];

    constructor(executor: Executor) {
        this.status = Status.PENDING;
        this.resolve = (value) => {
            if (this.status === Status.PENDING) {
                this.status = Status.FULLFILLED;
                this.resolveValue = value;
                // 执行回调函数，此时 this.resolveValue 已经有值
                this.resolveCallbacks.forEach(callback => callback());
            }
        };
        this.reject = (value) => {
            if (this.status === Status.PENDING) {
                this.status = Status.REJECTED;
                this.rejectValue = value;
                // 执行回调函数，此时 this.rejectValue 已经有值
                this.rejectCallbacks.forEach(callback => callback());
            }
        };

        try {
            executor(this.resolve, this.reject);
        } catch (err: any) {
            // 必须修改状态，否则后面的 this.reject()不会执行.
            this.status = Status.PENDING;
            this.reject("executor执行出错!");
            throw new Error("程序终止!");
        }
    }

    then(resolveFn?: ResolveType, rejectFn?: RejectType): Promise {
        return new Promise((resolve, reject) => {
            if (this.status === Status.FULLFILLED) {
                this.processResolve(resolveFn, resolve, reject);
            } else if (this.status === Status.REJECTED) {
                this.processReject(rejectFn, resolve, reject);
            } else {
                //pending状态下先不执行then的回调函数，而是保存起来.
                this.resolveCallbacks.push(() => {
                    this.processResolve(resolveFn, resolve, reject);
                });
                this.rejectCallbacks.push(() => {
                    this.processReject(rejectFn, resolve, reject);
                });
            }
        })
    }

    catch(rejectFn: RejectType): Promise {
        return this.then(undefined, rejectFn);
    }

    processResolve(resolveFn: ResolveType | undefined, resolve: ResolveType, reject: RejectType) {
        if (isFunction(resolveFn)) {
            let result = resolveFn(this.resolveValue);
            if (isPromise(result)) {
                result.then((resolveData) => {
                    resolve(resolveData);
                }, (rejectData) => {
                    reject(rejectData);
                });
            } else {
                resolve(result);
            }
        } else {
            resolve(undefined);
        }
    }

    processReject(rejectFn: RejectType | undefined, resolve: ResolveType, reject: RejectType) {
        if (isFunction(rejectFn)) {
            let result = rejectFn(this.rejectValue);
            if (isPromise(result)) {
                result.then((resolveData) => {
                    resolve(resolveData);
                }, (rejectData) => {
                    reject(rejectData);
                });
            } else {
                reject(result);
            }
        } else {
            reject(undefined);
        }
    }

    static all(promises: Promise[]): Promise {
        return new Promise((resolve, reject) => {
            let successResults: Array<any> = [];
            for (let i = 0; i < promises.length; i++) {
                // 使用立即执行函数解决i的问题
                (function (index: number) {
                    let promise = promises[index];
                    promise.then((resolveData) => {
                        successResults[index] = resolveData;
                        if (index === promises.length - 1) {
                            resolve(successResults);
                        }
                    }, (rejectData) => {
                        // 1个失败则全部失败
                        console.log(`第${index + 1}个promise执行失败，导致Promise.all执行失败!`);
                        reject(rejectData);
                    });
                })(i)
            }
        })
    }

    static race(promises: Promise[]): Promise {
        return new Promise((resolve, reject) => {
            let finished: boolean = false;
            for (let i = 0; i < promises.length; i++) {
                // 使用立即执行函数解决i的问题
                (function (index: number) {
                    let promise = promises[index];
                    promise.then((resolveData) => {
                        if (!finished) {
                            console.log(`第${index + 1}个promise率先改变状态，Promise.race执行完毕!`);
                            resolve(resolveData);
                            finished = true;
                        }
                    }, (rejectData) => {
                        if (!finished) {
                            console.log(`第${index + 1}个promise率先改变状态，Promise.race执行完毕!`);
                            reject(rejectData);
                            finished = true;
                        }
                    });
                })(i)
            }
        })
    }

    static resolve(data: any): Promise {
        return new Promise((resolve, reject) => {
            resolve(data);
        });
    }

    static reject(data: any): Promise {
        return new Promise((resolve, reject) => {
            reject(data);
        });
    }
}

function isObject(val: any): val is Record<any, any> {
    return val && typeof val === "object";
}

function isFunction(val: any): val is Function {
    return val && typeof val === "function";
}

function isPromise(val: any): val is Promise {
    return isObject(val) && isFunction(val.then);
}

export default Promise;
